
package com.google.code.sms.android.service;

import android.content.Context;
import android.graphics.Bitmap;
import org.apache.http.HttpResponse;

import com.google.code.sms.android.domain.Indexes;
import com.google.code.sms.android.domain.MediaElement;
import com.google.code.sms.android.domain.MediaFolder;
import com.google.code.sms.android.domain.Playlist;
import com.google.code.sms.android.domain.SearchCritera;
import com.google.code.sms.android.domain.SearchResult;
import com.google.code.sms.android.domain.Version;
import com.google.code.sms.android.util.CancellableTask;
import com.google.code.sms.android.util.LRUCache;
import com.google.code.sms.android.util.ProgressListener;
import com.google.code.sms.android.util.TimeLimitedCache;
import com.google.code.sms.android.util.Util;

import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author Sindre Mehus
 */
public class CachedMusicService implements MusicService {

    private static final int MEDIA_DIR_CACHE_SIZE = 20;
    private static final int TTL_MEDIA_DIR = 5 * 60; // Five minutes

    private final MusicService musicService;
    private final LRUCache<String, TimeLimitedCache<MediaElement>> cachedMediaDirectories;
    private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS);
    private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(60, TimeUnit.SECONDS);
    private final TimeLimitedCache<List<MediaFolder>> cachedMediaFolders = new TimeLimitedCache<List<MediaFolder>>(10 * 3600, TimeUnit.SECONDS);
    private String restUrl;

    public CachedMusicService(MusicService musicService) {
        this.musicService = musicService;
        cachedMediaDirectories = new LRUCache<String, TimeLimitedCache<MediaElement>>(MEDIA_DIR_CACHE_SIZE);
    }

    public void ping(Context context, ProgressListener progressListener) throws Exception {
        checkSettingsChanged(context);
        musicService.ping(context, progressListener);
    }

    public List<MediaFolder> getMediaFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
        checkSettingsChanged(context);
        if (refresh) {
            cachedMediaFolders.clear();
        }
        List<MediaFolder> result = cachedMediaFolders.get();
        if (result == null) {
            result = musicService.getMediaFolders(refresh, context, progressListener);
            cachedMediaFolders.set(result);
        }
        return result;
    }

    public Indexes getIndexes(String mediaFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
        checkSettingsChanged(context);
        if (refresh) {
            cachedIndexes.clear();
            cachedMediaFolders.clear();
            cachedMediaDirectories.clear();
        }
        Indexes result = cachedIndexes.get();
        if (result == null) {
            result = musicService.getIndexes(mediaFolderId, refresh, context, progressListener);
            cachedIndexes.set(result);
        }
        return result;
    }

    public MediaElement getMediaDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
        checkSettingsChanged(context);
        TimeLimitedCache<MediaElement> cache = refresh ? null : cachedMediaDirectories.get(id);
        MediaElement dir = cache == null ? null : cache.get();
        if (dir == null) {
            dir = musicService.getMediaDirectory(id, refresh, context, progressListener);
            cache = new TimeLimitedCache<MediaElement>(TTL_MEDIA_DIR, TimeUnit.SECONDS);
            cache.set(dir);
            cachedMediaDirectories.put(id, cache);
        }
        return dir;
    }

    public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception {
        return musicService.search(criteria, context, progressListener);
    }

    public MediaElement getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception {
        return musicService.getPlaylist(id, context, progressListener);
    }

    public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
        checkSettingsChanged(context);
        List<Playlist> result = refresh ? null : cachedPlaylists.get();
        if (result == null) {
            result = musicService.getPlaylists(refresh, context, progressListener);
            cachedPlaylists.set(result);
        }
        return result;
    }

    public void createPlaylist(String id, String name, List<MediaElement.Entry> entries, Context context, ProgressListener progressListener) throws Exception {
        musicService.createPlaylist(id, name, entries, context, progressListener);
    }
    
    public MediaElement getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
        return musicService.getAlbumList(type, size, offset, context, progressListener);
    }

    public Bitmap getCoverArt(Context context, MediaElement.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception {
        return musicService.getCoverArt(context, entry, size, saveToFile, progressListener);
    }

    public HttpResponse getDownloadInputStream(Context context, MediaElement.Entry song, long offset, String audioQuality, CancellableTask task) throws Exception {
        return musicService.getDownloadInputStream(context, song, offset, audioQuality, task);
    }

    public Version getLocalVersion(Context context) throws Exception {
        return musicService.getLocalVersion(context);
    }

    public String getVideoUrl(Context context, String id) throws Exception {
        return musicService.getVideoUrl(context, id);
    }
    
    public String getSubtitlesUrl(Context context, String id) throws Exception {
        return musicService.getSubtitlesUrl(context, id);
    }

    private void checkSettingsChanged(Context context) {
        String newUrl = Util.getRestUrl(context, null);
        if (!Util.equals(newUrl, restUrl)) {
            cachedMediaFolders.clear();
            cachedMediaDirectories.clear();
            cachedIndexes.clear();
            cachedPlaylists.clear();
            restUrl = newUrl;
        }
    }
}
